package com.sl.statistical.aspect

import android.util.Log
import com.sl.statistical.annotation.BaseMaiDianAnnotation
import com.sl.statistical.annotation.ParamsAnnotation
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Pointcut
import org.aspectj.lang.reflect.MethodSignature
import org.json.JSONObject
import java.lang.reflect.Method

/**
 * author： suliang
 * 2022/9/30 11:48
 * describe : 埋点类型对于的aspectJ, 多个类型，一个aspect类就可以，但为了解耦，可以每种对应一个aspect
 */
@Aspect
class MaiDianAspect {

    @Pointcut("execution(@com.sl.statistical.annotation.MaiDianData * * (..))")
    fun maiDianData() {
    }

    @Around("maiDianData()")
    fun mainDian(joinPoint: ProceedingJoinPoint) {

        //获取到方法的反射对象
        val methodSignature = joinPoint.signature as MethodSignature
        //获取到方法
        val method = methodSignature.method
        //获取到method上所有的注解
        val annotations = method.annotations

        //获取到方法中的参数的注解所有参数的注解
        val methodParameterAnnotation: Array<Annotation>? = getMethodParameterAnnotation(method)


        //创建基类注解类
        var baseMaiDianAnnotation: BaseMaiDianAnnotation? = null
        //循环方法注解
        run m@{
            annotations.forEach { annotation: Annotation ->
                //获取注解类上面的注解
                val annotationType: Class<*> = annotation.annotationClass.java
                baseMaiDianAnnotation =
                    annotationType.getAnnotation(BaseMaiDianAnnotation::class.java)
                if (baseMaiDianAnnotation == null) {
                    return@m
                }
            }
        }

        //为空，则相当于没有埋点类型
        baseMaiDianAnnotation?.let { base ->
            // 获取注解的埋点的type 和 id
            base.type
            base.actionId

            //拼接数据 注解类型、注解值、传递的参数
            val jsonObject = getJsonData(methodParameterAnnotation, joinPoint.args)
            //将收集的数据上传到服务器
            //框架
            //  1、内部实现网络框架，让用户通过注解将接口给传递过来，框架里面直接通过该接口进行上传
            //  2、自己用，可以自己写上传
            //  3、像权限申请一样将结果通过对应注解调用出去
            val result = "${base.type} _ ${base.actionId} _ ${jsonObject.toString()}"
            Log.e("sss--->埋点 ", result)
        }
        try {
            joinPoint.proceed()
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    /**
     * Get json data
     * 将参数的注解值和传递的参数值拼接成Json
     * @param methodParameterAnnotation 参数注解类型
     * @param args 参数值集合
     * @return
     */
    private fun getJsonData(
        methodParameterAnnotation: Array<Annotation>?,
        args: Array<Any>
    ): JSONObject? {
        if (methodParameterAnnotation.isNullOrEmpty()) {
            return null
        }
        val jsonObject = JSONObject()
        methodParameterAnnotation.forEachIndexed { index, annotation ->
            if (annotation is ParamsAnnotation) {        //是参数类型
                jsonObject.put(annotation.key, args[index])
            }
        }
        return jsonObject
    }

    /**
     * Get method parameter annotation
     * 获取到方法的参数的注解类型
     * @param method
     * @return Array<Annotation>? 参数的注解类型
     */
    private fun getMethodParameterAnnotation(method: Method): Array<Annotation>? {
        //为什么是二维数组？ 多个参数，每个参数可能有多个注解
        //返回类型 Annotation[][] 二维数组，意义： annotation[i][j]表示第i个参数，第j个注解类型
        val parameterAnnotation: Array<out Array<Annotation>> = method.parameterAnnotations
        if (parameterAnnotation.isEmpty()) return null
        val mutableListOf = mutableListOf<Annotation>()
        var i = 0
        parameterAnnotation.forEach { parameter: Array<out Annotation> ->
            parameter.forEach { annotation: Annotation ->
                Log.e("sss----->", annotation.toString())
                mutableListOf.add(annotation)
                i++
            }
        }
        return if (mutableListOf.isEmpty()) {
            null
        } else mutableListOf.toTypedArray()
    }

}